import yaml
from typing import List, Dict, Any, Tuple, Union


def modbus_crc(data: bytes) -> int:
    """计算Modbus RTU CRC校验值"""
    crc = 0xFFFF
    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x0001:
                crc >>= 1
                crc ^= 0xA001
            else:
                crc >>= 1
    return crc & 0xFFFF


def build_modbus_message(slave_id: int, function_code: int, address: int, value: int) -> Tuple[bytes, str, List[int]]:
    """构建Modbus消息并计算CRC（支持功能码1~16）"""
    if not (1 <= function_code <= 16):
        raise ValueError(f"功能码{function_code}超出范围（1~16）")

    data = bytearray([slave_id, function_code])
    data.extend(address.to_bytes(2, 'big'))  # 起始地址（2字节，大端）

    # 按功能码处理数据段（参考Modbus协议规范）
    if function_code == 1:  # 读线圈状态
        data.extend((0x0001).to_bytes(2, 'big'))  # 固定读1个线圈
    elif function_code == 2:  # 读离散输入
        data.extend((0x0001).to_bytes(2, 'big'))  # 固定读1个输入
    elif function_code == 3:  # 读保持寄存器
        data.extend((0x0001).to_bytes(2, 'big'))  # 固定读1个寄存器
    elif function_code == 4:  # 读输入寄存器
        data.extend((0x0001).to_bytes(2, 'big'))  # 固定读1个寄存器
    elif function_code == 5:  # 写单个线圈
        data.extend(value.to_bytes(2, 'big'))  # 0xFF00=ON，0x0000=OFF
    elif function_code == 6:  # 写单个寄存器
        data.extend(value.to_bytes(2, 'big'))  # 写入值
    elif function_code == 7:  # 读异常状态
        pass  # 无额外数据
    elif function_code == 8:  # 诊断
        data.extend((0x0000).to_bytes(2, 'big'))  # 子功能码
        data.extend(value.to_bytes(2, 'big'))  # 诊断数据
    elif function_code == 9:  # 编程器模式（已废弃）
        data.extend(value.to_bytes(2, 'big'))
    elif function_code == 10:  # 获取通信事件计数
        pass  # 无额外数据
    elif function_code == 11:  # 获取通信事件记录
        data.extend((0x0001).to_bytes(2, 'big'))  # 固定读1条记录
    elif function_code == 12:  # 编程器访问（已废弃）
        data.extend(value.to_bytes(2, 'big'))
    elif function_code == 13:  # 16位CRC校验
        data.extend((0x0001).to_bytes(2, 'big'))  # 数据长度
    elif function_code == 14:  # 读文件记录（简化版）
        data.extend((0x0000).to_bytes(2, 'big'))  # 文件号
        data.extend((0x0000).to_bytes(2, 'big'))  # 记录号
        data.extend(value.to_bytes(2, 'big'))  # 记录长度
    elif function_code == 15:  # 写多个线圈
        data.extend((0x0001).to_bytes(2, 'big'))  # 线圈数量
        data.extend((0x01).to_bytes(1, 'big'))  # 字节数
        data.extend((0x01 if value else 0x00).to_bytes(1, 'big'))  # 状态
    elif function_code == 16:  # 写多个寄存器
        data.extend((0x0001).to_bytes(2, 'big'))  # 寄存器数量
        data.extend((0x02).to_bytes(1, 'big'))  # 字节数
        data.extend(value.to_bytes(2, 'big'))  # 寄存器值

    # 计算CRC
    crc = modbus_crc(data)
    data.extend(crc.to_bytes(2, 'little'))

    hex_data = data.hex()
    byte_list = list(data)
    return data, hex_data, byte_list


def parse_yaml_config(file_path: str) -> Dict[str, Any]:
    """解析YAML配置文件（严格检查配置完整性）"""
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            config = yaml.safe_load(file)
        if not config or 'settings' not in config:
            raise ValueError("配置文件缺少'settings'节点")
        return config
    except Exception as e:
        print(f"解析配置文件失败: {str(e)}")
        raise


def generate_device_messages(device_config: Dict[str, Any]) -> List[Dict[str, Any]]:
    """生成设备电源控制消息（适配新配置：Sts_Power/Sensor_Power）
    - name: 对应配置中的设备名（如Sts_Power、Sensor_Power）
    - device: 固定为PowerDevice（用于分组）
    """
    messages = []
    # 遍历device下的所有具体设备（Sts_Power、Sensor_Power）
    for device_name in device_config.keys():
        # 跳过linux/flash节点（单独由对应函数处理）
        if device_name in ['linux', 'flash']:
            continue

        device = device_config[device_name]
        required_keys = ['code', 'slave_id', 'address']
        for key in required_keys:
            if key not in device:
                raise ValueError(f"电源设备'{device_name}'配置缺失必要参数: {key}")

        function_code = device['code']  # 新配置中code是单个值（功能码5）
        slave_id = device['slave_id']
        address = device['address']

        # 处理电源的ON/OFF（功能码5：写单个线圈）
        for is_on in [True, False]:
            value = 0xFF00 if is_on else 0x0000  # 0xFF00=ON，0x0000=OFF
            _, hex_data, _ = build_modbus_message(slave_id, function_code, address, value)
            messages.append({
                'device': 'PowerDevice',  # 固定设备类型（用于分组）
                'name': device_name,  # 关键：对应配置中的设备名（如Sts_Power）
                'type': 'power_control',
                'action': 'press' if is_on else 'release',
                'function_code': function_code,
                'slave_id': slave_id,
                'address': address,
                'hex_data': hex_data
            })

    return messages


def generate_key_messages(key_config: Dict[str, Any]) -> List[Dict[str, Any]]:
    """生成按键控制消息
    - name: 对应配置中的按键名（如One、Two、Up）
    - device: 固定为Key（用于分组）
    """
    messages = []
    if not key_config:
        print("警告: 按键配置为空，跳过按键消息生成")
        return messages

    for key_name, key_info in key_config.items():
        required_keys = ['code', 'slave_id', 'address']
        for key in required_keys:
            if key not in key_info:
                raise ValueError(f"按键'{key_name}'配置缺失必要参数: {key}")

        code = key_info['code']
        slave_id = key_info['slave_id']
        address = key_info['address']

        # 处理按键的Press/Release
        for is_press in [True, False]:
            value = 0x0001 if is_press else 0x0000
            _, hex_data, _ = build_modbus_message(slave_id, code, address, value)
            messages.append({
                'device': 'Key',  # 固定设备类型（用于分组）
                'name': key_name,  # 关键：对应配置中的按键名（如One）
                'type': 'key_control',
                'action': 'press' if is_press else 'release',
                'function_code': code,
                'slave_id': slave_id,
                'address': address,
                'hex_data': hex_data
            })
    return messages


def generate_discrete_messages(discrete_config: Dict[str, Any]) -> List[Dict[str, Any]]:
    """生成离散输入消息（读操作）
    - name: 对应配置中的离散输入名（如STS_Rx_Power_Status）
    - device: 固定为Discrete（用于分组）
    """
    messages = []
    if not discrete_config:
        print("警告: 配置中'discrete'节点为空，跳过离散输入消息生成")
        return messages

    for input_name, input_info in discrete_config.items():
        # 检查层级（不超过三级：discrete -> input_name -> 参数）
        if not isinstance(input_info, dict):
            raise ValueError(f"离散输入'{input_name}'配置格式错误，应为字典类型")
        for key, value in input_info.items():
            if isinstance(value, dict):
                raise ValueError(f"离散输入'{input_name}'的'{key}'参数包含嵌套字典，超出三级结构限制")

        # 检查必要参数
        required_keys = ['code', 'slave_id', 'address']
        for key in required_keys:
            if key not in input_info:
                raise ValueError(f"离散输入'{input_name}'配置缺失必要参数: {key}")

        code = input_info['code']
        slave_id = input_info['slave_id']
        address = input_info['address']

        # 读操作（功能码2：读离散输入）
        _, hex_data, _ = build_modbus_message(slave_id, code, address, 0x0001)
        messages.append({
            'device': 'Discrete',  # 固定设备类型（用于分组）
            'name': input_name,  # 关键：对应配置中的离散输入名（如STS_Rx_Power_Status）
            'type': 'discrete_read',
            'action': 'only',
            'function_code': code,
            'slave_id': slave_id,
            'address': address,
            'hex_data': hex_data
        })
    return messages


def generate_linux_messages(device_config: Dict[str, Any]) -> List[Dict[str, Any]]:
    """生成Linux设备控制消息（适配新配置：linux直接在device下）
    - name: 对应配置中的Linux输入名（如linux_Data、linux_Vcc）
    - device: 固定为Linux（用于分组）
    """
    messages = []
    # 检查device下是否有linux节点
    if 'linux' not in device_config:
        print("警告: 配置中'device'节点下未定义'linux'，跳过Linux消息生成")
        return messages

    linux_config = device_config['linux']
    for input_name, input_info in linux_config.items():
        required_keys = ['code', 'slave_id', 'address']
        for key in required_keys:
            if key not in input_info:
                raise ValueError(f"Linux输入'{input_name}'配置缺失必要参数: {key}")

        code = input_info['code']
        slave_id = input_info['slave_id']
        address = input_info['address']

        # 处理Linux的Press/Release
        for is_press in [True, False]:
            value = 0x0001 if is_press else 0x0000
            _, hex_data, _ = build_modbus_message(slave_id, code, address, value)
            messages.append({
                'device': 'Linux',  # 固定设备类型（用于分组）
                'name': f"linux_{input_name}",  # 关键：拼接名称（如linux_Data，便于区分）
                'type': 'linux_control',
                'action': 'press' if is_press else 'release',
                'function_code': code,
                'slave_id': slave_id,
                'address': address,
                'hex_data': hex_data
            })
    return messages


def generate_flash_messages(device_config: Dict[str, Any]) -> List[Dict[str, Any]]:
    """生成Flash设备控制消息（适配新配置：flash直接在device下）
    - name: 对应配置中的Flash输入名（如flash_Data、flash_Vcc）
    - device: 固定为Flash（用于分组）
    """
    messages = []
    # 检查device下是否有flash节点
    if 'flash' not in device_config:
        print("警告: 配置中'device'节点下未定义'flash'，跳过Flash消息生成")
        return messages

    flash_config = device_config['flash']
    for input_name, input_info in flash_config.items():
        required_keys = ['code', 'slave_id', 'address']
        for key in required_keys:
            if key not in input_info:
                raise ValueError(f"Flash输入'{input_name}'配置缺失必要参数: {key}")

        code = input_info['code']
        slave_id = input_info['slave_id']
        address = input_info['address']

        # 处理Flash的Press/Release
        for is_press in [True, False]:
            value = 0x0001 if is_press else 0x0000
            _, hex_data, _ = build_modbus_message(slave_id, code, address, value)
            messages.append({
                'device': 'Flash',  # 固定设备类型（用于分组）
                'name': f"flash_{input_name}",  # 关键：拼接名称（如flash_Data，便于区分）
                'type': 'flash_control',
                'action': 'press' if is_press else 'release',
                'function_code': code,
                'slave_id': slave_id,
                'address': address,
                'hex_data': hex_data
            })
    return messages


def generate_check_messages(check_config: Dict[str, Any]) -> List[Dict[str, Any]]:
    """生成离散输入消息（读操作）
    - name: 对应配置中的离散输入名（如STS_Rx_Power_Status）
    - device: 固定为check（用于分组）
    """
    messages = []
    if not check_config:
        print("警告: 配置中'check'节点为空，跳过离散输入消息生成")
        return messages

    for input_name, input_info in check_config.items():
        # 检查层级（不超过三级：discrete -> input_name -> 参数）
        if not isinstance(input_info, dict):
            raise ValueError(f"离散输入'{input_name}'配置格式错误，应为字典类型")
        for key, value in input_info.items():
            if isinstance(value, dict):
                raise ValueError(f"离散输入'{input_name}'的'{key}'参数包含嵌套字典，超出三级结构限制")

        # 检查必要参数
        required_keys = ['code', 'slave_id', 'address']
        for key in required_keys:
            if key not in input_info:
                raise ValueError(f"离散输入'{input_name}'配置缺失必要参数: {key}")

        code = input_info['code']
        slave_id = input_info['slave_id']
        address = input_info['address']

        # 读操作（功能码2：读离散输入）
        _, hex_data, _ = build_modbus_message(slave_id, code, address, 0x0001)
        messages.append({
            'device': 'Check',  # 固定设备类型（用于分组）
            'name': input_name,  # 关键：对应配置中的离散输入名（如STS_Rx_Power_Status）
            'type': 'check_read',
            'action': 'only',
            'function_code': code,
            'slave_id': slave_id,
            'address': address,
            'hex_data': hex_data
        })
    return messages

def generate_init_messages(check_config: Dict[str, Any]) -> List[Dict[str, Any]]:
    """生成离散输入消息（读操作）
    - name: 对应配置中的离散输入名（如STS_Rx_Power_Status）
    - device: 固定为check（用于分组）
    """
    messages = []
    if not check_config:
        print("警告: 配置中'init'节点为空，跳过离散输入消息生成")
        return messages

    for input_name, input_info in check_config.items():
        # 检查层级（不超过三级：discrete -> input_name -> 参数）
        if not isinstance(input_info, dict):
            raise ValueError(f"离散输入'{input_name}'配置格式错误，应为字典类型")
        for key, value in input_info.items():
            if isinstance(value, dict):
                raise ValueError(f"离散输入'{input_name}'的'{key}'参数包含嵌套字典，超出三级结构限制")

        # 检查必要参数
        required_keys = ['code', 'slave_id', 'address']
        for key in required_keys:
            if key not in input_info:
                raise ValueError(f"离散输入'{input_name}'配置缺失必要参数: {key}")

        code = input_info['code']
        slave_id = input_info['slave_id']
        address = input_info['address']

        # 读操作（功能码2：读离散输入）
        _, hex_data, _ = build_modbus_message(slave_id, code, address, 0x0001)
        messages.append({
            'device': 'Init',  # 固定设备类型（用于分组）
            'name': input_name,  # 关键：对应配置中的离散输入名（如STS_Rx_Power_Status）
            'type': 'init_read',
            'action': 'only',
            'function_code': code,
            'slave_id': slave_id,
            'address': address,
            'hex_data': hex_data
        })
    return messages

def init_modbus_lists(config):
    """初始化modbus消息列表（整合所有类型消息，均含name字段）"""
    try:
        settings = config['settings']
        # 检查必要节点（适配新配置：device/key/discrete同级）
        required_nodes = ['device', 'key', 'discrete']
        for node in required_nodes:
            if node not in settings:
                raise ValueError(f"配置的'settings'节点中缺失'{node}'节点")

        device_config = settings['device']
        key_config = settings['key']
        discrete_config = settings['discrete']
        check_config = settings['check']
        init_config = settings['init']

        # 生成各类消息（均已添加name字段）
        power_messages = generate_device_messages(device_config)
        key_messages = generate_key_messages(key_config)
        discrete_messages = generate_discrete_messages(discrete_config)
        linux_messages = generate_linux_messages(device_config)
        flash_messages = generate_flash_messages(device_config)
        check_messages = generate_check_messages(check_config)
        init_messages = generate_init_messages(init_config)

        # 合并所有消息
        return (power_messages, key_messages, discrete_messages, linux_messages, flash_messages, check_messages, init_messages)

    except Exception as e:
        print(f"初始化消息列表失败: {str(e)}")
        return []


def split_modbus_by_device(modbus_list):
    """按设备类型分组（基于device字段），便于批量查找"""
    device_dict = {}
    for msg in modbus_list:
        device_type = msg.get('device')
        if not device_type:
            print(f"跳过无效消息（无device字段）: {msg.get('name', '未知消息')}")
            continue
        # 按设备类型分组（如PowerDevice、Key、Discrete）
        if device_type not in device_dict:
            device_dict[device_type] = []
        device_dict[device_type].append(msg)
    return device_dict

def get_settings(config):
    config = parse_yaml_config(config)
    messages = init_modbus_lists(config)

    return messages


if __name__ == "__main__":
    # 读取配置并生成消息
    try:
        # 注意：请确保配置文件路径正确（根据实际情况调整）
        config = parse_yaml_config("../configs/settings.yaml")
        modbus_messages = init_modbus_lists(config)

        # 1. 按设备类型分组（便于批量查找）
        device_group = split_modbus_by_device(modbus_messages)
        print("\n" + "=" * 50)
        print("按设备类型分组结果：")
        for dev_type, msgs in device_group.items():
            print(f"\n【{dev_type}】（共{len(msgs)}条）：")
            for msg in msgs:
                # 打印关键信息：name + 动作 + 地址 + 十六进制指令
                print(f"  {msg['name']:20} | {msg['action']:8} | 地址:{msg['address']:3} | 指令:{msg['hex_data']}")

        # 2. 按name精确查找示例（比如查找"Sts_Power"相关消息）
        print("\n" + "=" * 50)
        target_name = "Sts_Power"
        target_msgs = [msg for msg in modbus_messages if msg['name'] == target_name]
        print(f"精确查找 '{target_name}' 相关消息（共{len(target_msgs)}条）：")
        for msg in target_msgs:
            print(f"  类型：{msg['type']} | 动作：{msg['action']} | 指令：{msg['hex_data']}")

    except FileNotFoundError:
        print("错误：配置文件未找到，请检查路径是否正确！")
    except Exception as e:
        print(f"运行错误: {str(e)}")